Coverage Report

Created: 2026-06-19 16:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\csshw\csshw\src\protocol\serialization.rs
Line
Count
Source
1
use windows::Win32::System::Console::{INPUT_RECORD_0, KEY_EVENT_RECORD, KEY_EVENT_RECORD_0};
2
3
use crate::protocol::{
4
    ClientState, DaemonToClientMessage, FRAMED_HIGHLIGHT_LENGTH, FRAMED_INPUT_RECORD_LENGTH,
5
    FRAMED_KEEP_ALIVE_LENGTH, FRAMED_STATE_CHANGE_LENGTH, SERIALIZED_INPUT_RECORD_0_LENGTH,
6
    SERIALIZED_PID_LENGTH, TAG_HIGHLIGHT, TAG_INPUT_RECORD, TAG_KEEP_ALIVE, TAG_STATE_CHANGE,
7
};
8
9
/// Serialize a [KEY_EVENT_RECORD_0] into a `Vec<u8>` using custom binary format.
10
///
11
/// Returns the u16 `UnicodeChar` as `Vec<u8>`in little-endian format.
12
1
pub fn serialize_key_event_record_0(record: &KEY_EVENT_RECORD_0) -> Vec<u8> {
13
1
    return unsafe { record.UnicodeChar }.to_le_bytes().to_vec();
14
1
}
15
16
/// Serialize a [KEY_EVENT_RECORD] into a `Vec<u8>`using custom binary format.
17
///
18
/// Layout: [1 byte KeyDown][2 bytes RepeatCount][2 bytes VirtualKeyCode]
19
///         [2 bytes VirtualScanCode][2 bytes UnicodeChar][4 bytes ControlKeyState]
20
7
pub fn serialize_key_event_record(record: &KEY_EVENT_RECORD) -> Vec<u8> {
21
7
    let mut buf = Vec::with_capacity(SERIALIZED_INPUT_RECORD_0_LENGTH);
22
23
    // KeyDown as u8 (1 byte)
24
7
    buf.push(if record.bKeyDown.as_bool() { 1u8 } else { 
0u80
});
25
26
    // RepeatCount as u16 LE (2 bytes)
27
7
    buf.extend_from_slice(&record.wRepeatCount.to_le_bytes());
28
29
    // VirtualKeyCode as u16 LE (2 bytes)
30
7
    buf.extend_from_slice(&record.wVirtualKeyCode.to_le_bytes());
31
32
    // VirtualScanCode as u16 LE (2 bytes)
33
7
    buf.extend_from_slice(&record.wVirtualScanCode.to_le_bytes());
34
35
    // UnicodeChar as u16 LE (2 bytes)
36
7
    buf.extend_from_slice(&unsafe { record.uChar.UnicodeChar }.to_le_bytes());
37
38
    // ControlKeyState as u32 LE (4 bytes)
39
7
    buf.extend_from_slice(&record.dwControlKeyState.to_le_bytes());
40
41
7
    return buf;
42
7
}
43
44
/// Serialize an [INPUT_RECORD_0].`KeyEvent` into a `Vec<u8>`using custom binary format.
45
///
46
/// Panics if the [INPUT_RECORD_0] is not a `KeyEvent`.
47
6
pub fn serialize_input_record_0(record: &INPUT_RECORD_0) -> Vec<u8> {
48
6
    return serialize_key_event_record(&unsafe { record.KeyEvent });
49
6
}
50
51
/// Serialize a process id into its little-endian byte representation used by
52
/// the named-pipe PID handshake.
53
10
pub fn serialize_pid(pid: u32) -> [u8; SERIALIZED_PID_LENGTH] {
54
10
    return pid.to_le_bytes();
55
10
}
56
57
/// Serialize a [`ClientState`] into its single-byte wire representation.
58
///
59
/// # Arguments
60
///
61
/// * `state` - The client state to serialize.
62
///
63
/// # Returns
64
///
65
/// The state's `#[repr(u8)]` discriminant, used as the payload of a tagged
66
/// [`crate::protocol::TAG_STATE_CHANGE`] frame.
67
16
pub fn serialize_client_state(state: ClientState) -> u8 {
68
16
    return state as u8;
69
16
}
70
71
/// Serialize a highlight flag into its single-byte wire representation.
72
///
73
/// # Arguments
74
///
75
/// * `highlighted` - `true` if the client is the daemon's currently
76
///                   selected submenu client, `false` otherwise.
77
///
78
/// # Returns
79
///
80
/// `1` for `true`, `0` for `false`. Used as the payload of a tagged
81
/// [`crate::protocol::TAG_HIGHLIGHT`] frame.
82
14
pub fn serialize_highlight(highlighted: bool) -> u8 {
83
14
    return if highlighted { 
15
} else {
09
};
84
14
}
85
86
/// Serialize a [`DaemonToClientMessage`] into its tagged-envelope wire
87
/// representation.
88
///
89
/// The first byte of the returned vector is the tag identifying the variant;
90
/// the remaining bytes (if any) are the variant's payload.
91
///
92
/// # Arguments
93
///
94
/// * `msg` - The message to serialize.
95
///
96
/// # Returns
97
///
98
/// A vector containing the framed wire bytes ready to be written to the
99
/// daemon's named pipe.
100
13
pub fn serialize_daemon_to_client_message(msg: &DaemonToClientMessage) -> Vec<u8> {
101
13
    match msg {
102
4
        DaemonToClientMessage::InputRecord(record) => {
103
4
            let mut buf = Vec::with_capacity(FRAMED_INPUT_RECORD_LENGTH);
104
4
            buf.push(TAG_INPUT_RECORD);
105
4
            buf.extend_from_slice(&serialize_input_record_0(record));
106
4
            return buf;
107
        }
108
2
        DaemonToClientMessage::StateChange(state) => {
109
2
            let mut buf = Vec::with_capacity(FRAMED_STATE_CHANGE_LENGTH);
110
2
            buf.push(TAG_STATE_CHANGE);
111
2
            buf.push(serialize_client_state(*state));
112
2
            return buf;
113
        }
114
3
        DaemonToClientMessage::Highlight(highlighted) => {
115
3
            let mut buf = Vec::with_capacity(FRAMED_HIGHLIGHT_LENGTH);
116
3
            buf.push(TAG_HIGHLIGHT);
117
3
            buf.push(serialize_highlight(*highlighted));
118
3
            return buf;
119
        }
120
        DaemonToClientMessage::KeepAlive => {
121
4
            let mut buf = Vec::with_capacity(FRAMED_KEEP_ALIVE_LENGTH);
122
4
            buf.push(TAG_KEEP_ALIVE);
123
4
            return buf;
124
        }
125
    }
126
13
}